﻿using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Text.Json.Serialization.Metadata;
using DimensionsHelper.CommandLine.ConsoleTracer;
using DimensionsHelper.CommandLine.PreTableRender;
using DimensionsHelper.Common.Contracts.TableFormatter.Options;
using DimensionsHelper.Common.Contracts.DataCollector;
using DimensionsHelper.Common.Contracts.DataCollector.Options;
using DimensionsHelper.Common.Contracts.Metadata;
using DimensionsHelper.Common.Utils;
using DimensionsHelper.Services.TableFormatter;
using DimensionsHelper.Services.CsvReader;
using DimensionsHelper.Services.DataCollector;
using DimensionsHelper.Services.Excel.EPPlus;
using DimensionsHelper.Services.Metadata;

namespace DimensionsHelper.CommandLine;

public static class Program
{
    private const string DefaultOutputName = "output";
    private const string AppendDateSuffixOptionName = "-date";
    private static readonly string CurrentDirectory = Directory.GetCurrentDirectory();

    public static void Main(string[] args)
    {
        var reader = new ArgumentsReader(args.ToList());

        // Console.CursorVisible = false;
        TracerProgressBar.ProgressColor = Tracer.LogForeground;
        TracerProgressBar.ProgressInformationColor = Tracer.LogForeground;

        switch (reader.OptionName.ToLower())
        {
            case "pretableexecutor":
                RunPreTableRender(reader);
                break;

            case "csvformatter":
                RunCsvFormatter(reader);
                break;

            case "datacollector":
                RunExcelDataCollector(reader);
                break;
                
            case "metadataserializer":
                RunMetadataSerializer(reader);
                break;
        }
    }


    private static void Warn(string message)
    {
        Tracer.Warn("[Warn] {0}", message);
    }


    private static void Error(string message)
    {
        Tracer.Error("[Program] {0}", message);
    }


    private static List<string> GetAllFileNamesOfExtension(string extension, string directory)
    {
        if (!Directory.Exists(directory))
        {
            Error($"路径：{directory} 不存在.");
            return [];
        }

        List<string> files = Directory.GetFiles(directory)
            .Where(d => d.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
            .ToList();

        foreach (string file in files)
        {
            Tracer.Log("找到文件 : {0}", file);
        }

        return files;
    }


    private static List<string> GetAllFileNamesOfExtension(string extension)
    {
        return GetAllFileNamesOfExtension(extension, Directory.GetCurrentDirectory());
    }


    private static List<string> GetInputFiles(ArgumentsReader reader, string extension)
    {
        if (reader.TryGetArgValue("-input", out string? inputFileNames))
        {
            var result = new List<string>();

            foreach (string dir in inputFileNames.Split(';'))
            {
                if (File.Exists(dir))
                {
                    result.Add(dir);
                }
                else if (Directory.Exists(dir))
                {
                    result.AddRange(GetAllFileNamesOfExtension(extension, dir));
                }
            }

            return result;
        }

        return GetAllFileNamesOfExtension(extension);
    }


    private static string GetDateSuffix()
    {
        return DateTime.Now.ToString("yyyy MM dd").Replace(" ", "");
    }


    private static string GetOutputFile(ArgumentsReader reader, string extension, bool dateSuffix)
    {
        var builder = new StringBuilder();

        if (reader.TryGetArgValue("-output", out string? outputFileName) &&
            !string.IsNullOrEmpty(outputFileName))
        {
            if (outputFileName.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
            {
                builder.Append(outputFileName.AsSpan(0, outputFileName.Length - 5));
            }
            else
            {
                builder.Append(outputFileName);
            }

            if (dateSuffix)
            {
                if (!outputFileName.EndsWith('-'))
                {
                    builder.Append('-');
                }

                builder.Append(GetDateSuffix());
            }

            builder.Append(extension);
            return builder.ToString();
        }

        builder.Append(DefaultOutputName);

        if (dateSuffix)
        {
            builder.Append('-');
            builder.Append(GetDateSuffix());
        }

        builder.Append(extension);
        return builder.ToString();
    }


    private static T TryParseJson<
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor |
                                    DynamicallyAccessedMemberTypes.PublicProperties)]
        T>(ArgumentsReader reader, JsonTypeInfo<T> jsonTypeInfo) where T : class, new()
    {
        T res = new();

        try
        {
            if (string.IsNullOrEmpty(reader.JsonArgValue))
            {
                Warn($"参数 {ArgumentsReader.JsonArgumentName} 没有有效值，使用默认配置.");
            }
            else
            {
                res = JsonHelper.ReadJson(reader.JsonArgValue, jsonTypeInfo) ??
                      throw new Exception($"JSON 反序列化失败 : {reader.JsonArgValue}");
            }
        }
        catch (Exception e)
        {
            Error(e.Message);
        }

        return res;
    }


    private static ExcelWorkbookOperator GetWorkbook(string fileName)
    {
        if (File.Exists(fileName))
        {
            File.Delete(fileName);
        }

        var workbook = new ExcelWorkbookOperator();
        workbook.Create(fileName);
        return workbook;
    }


    private static string GetFullPath(string filePath)
    {
        return !Path.IsPathRooted(filePath) ? Path.GetFullPath(filePath, CurrentDirectory) : filePath;
    }


    private static void RunPreTableRender(ArgumentsReader reader)
    {
        PreTableOptions preExecutorOptions = !string.IsNullOrEmpty(reader.JsonArgValue)
                        ? TryParseJson(reader, CommandLineJsonContext.Default.PreTableOptions)
                        : CommandLineArgumentContext.Default.Resolve<PreTableOptions>(reader.GetArgValue);
        PreTableExecutor.Execute(preExecutorOptions);
    }


    private static void RunCsvFormatter(ArgumentsReader reader)
    {
        // -input
        List<string> csvInputs = GetInputFiles(reader, ".csv");

        if (csvInputs.Count == 0)
        {
            Error("输入文件为空.");
            return;
        }

        string output = GetOutputFile(reader, ".xlsx",
            reader.TryGetArgValue(AppendDateSuffixOptionName, out _));
        TableFormatterOptions csvFormatterOptions = TryParseJson(reader,
            ServiceJsonContext.Default.TableFormatterOptions);
        var formatter = new TableFormatter(csvFormatterOptions, new CsvStreamReader());
        formatter.ReportService = Tracer.GetService(reader.ReportLevel, formatter.Section);
        formatter.Execute(GetWorkbook(output), csvInputs);
    }


    private static void RunExcelDataCollector(ArgumentsReader reader)
    {
        // -json
        if (string.IsNullOrEmpty(reader.JsonArgValue))
        {
            Error($"参数 {ArgumentsReader.JsonArgumentName} 不可忽略.");
            return;
        }

        // -input
        var collectorInputs = new List<(int, string, string?)>();
        string? collectorInputArg = reader.GetArgValue("-input");

        if (string.IsNullOrEmpty(collectorInputArg))
        {
            Error("参数 -input 不可忽略.");
            return;
        }

        // [Index]:[File1,File2,..][?expression]
        foreach (string arg in collectorInputArg.Split(';'))
        {
            string[] indexPair = arg.Split(':');
            if (indexPair.Length != 2 ||
                string.IsNullOrWhiteSpace(indexPair[0]) ||
                !int.TryParse(indexPair[0], out int fileIndex))
            {
                Warn($"-input 参数值 '{arg}' 无效, 已跳过.");
                continue;
            }

            string? expr = null;
            string files = indexPair[1];
            string[] fileWithExpr = files.Split('?');

            if (fileWithExpr.Length >= 2)
            {
                files = fileWithExpr[0];
                expr = fileWithExpr[1];
            }

            foreach (string file in files.Split(','))
            {
                string fullPath = GetFullPath(file);
                // 判断是否是文件夹
                if (Directory.Exists(fullPath))
                {
                    Tracer.Log("找到文件夹 : {0}", fullPath);
                    collectorInputs.AddRange(GetAllFileNamesOfExtension(".xlsx", fullPath).Select(excelFile => (fileIndex, excelFile, expr)));
                }
                else
                {
                    collectorInputs.Add((fileIndex, fullPath, expr));
                }
            }
        }

        if (collectorInputs.Count == 0)
        {
            Error("未找到有效的输入文件.");
            return;
        }

        CollectorOptions options = TryParseJson(reader, ServiceJsonContext.Default.CollectorOptions);
        // export
        string exportName = reader.GetArgValue("-export") ?? $"{DefaultOutputName}.xlsx";
        // .xlsx
        if (string.IsNullOrEmpty(Path.GetExtension(exportName)))
        {
            exportName += ".xlsx";
        }

        // full path
        exportName = GetFullPath(exportName);

        var collector = new ExcelDataCollector(options, null);
        collector.ReportService = Tracer.GetService(reader.ReportLevel, collector.Section);

        // -summary
        if (reader.TryGetArgValue("-summary", out _))
        {
            collector.Writer = new ExcelSummaryWriter(null);
        }

        ExcelWorkbookOperator targetWorkbook = GetWorkbook(exportName);

        Tracer.TraceReport(collector, c =>
        {
            c.Collect(collectorInputs.Select(e => new CollectorInputFile(
                e.Item1, e.Item2, e.Item3, new ExcelWorkbookOperator())));
            c.Export(targetWorkbook);
        });
    }


    private static void RunMetadataSerializer(ArgumentsReader reader)
    {
        var inputs = GetInputFiles(reader, ".mdd");
        if (inputs.Count == 0)
        {
            return;
        }
        
        // -outputs
        var outputs = reader.GetArgValue("-output");
        if (string.IsNullOrEmpty(outputs))
        {
            return;
        }

        // -list
        bool listFields = reader.TryGetArgValue("-list", out _);
        // options
        var options = new MetadataSerializeOptions
        {
            Indent = 4,
            ElementFlags = reader.TryGetArgValue("-simple", out _) 
                ? MetadataElementFlags.None : MetadataElementFlags.All
        };

        var outputNames = outputs.Split(';');
        for (int i = 0; i < outputNames.Length; i++)
        {
            if (i >= inputs.Count)
            {
                break;
            }

            var input = inputs[i];
            var metadata = new MetadataDocument();
            metadata.Open(GetFullPath(input));
            var serializer = new MetadataSerializer(options);
            var text = serializer.Serialize(metadata);
            var fullPath = GetFullPath(outputNames[i]);
            FileHelper.DeleteFile(fullPath);
            File.WriteAllText(fullPath, text);

            if (listFields)
            {
                // 文件名是 "输入文件名+_fields.txt"
                var inputFileName = Path.GetFileNameWithoutExtension(input);
                var targetName = Path.Join(Path.GetDirectoryName(input), $"{inputFileName}_fields.txt");
                File.WriteAllText(targetName, string.Join("\r\n", 
                    MetadataSerializer.GetFieldList(metadata, reader.TryGetArgValue("-level", out _))));
            }
        }
    }
    
}